﻿/* 
This file is part of Ingo Karstein's Web based PowerShell Console project

**Do not remove this comment**

Please see the project homepage at CodePlex:
  http://webpsconsole.codeplex.com/

Please see my blog:
  http://ikarstein.wordpress.com

Licence: GPLv2
    Ingo Karstein's SharePoint 2010 Custom Ribbon Demo
    Copyright (C) 2011  Ingo Karstein, ikarstein@hotmail.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/



using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using PowerShell = System.Management.Automation.PowerShell;
using System.Globalization;
using System.Management.Automation.Host;
using System.Security;
using System.Reflection;
using System.Diagnostics;
using System.Threading;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System.IO;

namespace ik.PowerShell.WebPSConsole
{
    internal class WebPSConsoleHostRawUI : PSHostRawUserInterface
    {
        private Server server = null;
        private WebPSConsoleHostUI ui = null;

        public WebPSConsoleHostRawUI(Server server, WebPSConsoleHostUI ui)
        {
            this.server = server;
            this.ui = ui;

            this.server.OnResize += new Server.ResizeHandler(parent_OnResize);
        }

        void parent_OnResize(int width, int height)
        {
            WindowSize = new Size(width, height);
            BufferSize = new Size(width, Buffer.MAXROWS);
        }

        private ConsoleColor _backgroundColor = ConsoleColor.Black;
        public override ConsoleColor BackgroundColor
        {
            get
            {
                return _backgroundColor;
            }
            set
            {
                _backgroundColor = value;
            }
        }

        public override Size BufferSize
        {
            get
            {
                return server.BufferSize;
            }
            set
            {
                server.BufferSize = value;
            }
        }

        public override Coordinates CursorPosition
        {
            get
            {
                return server.CursorPosition;
            }
            set
            {
                server.CursorPosition = value;
            }
        }

        private int _cursorSize = 8;
        public override int CursorSize
        {
            get
            {
                return _cursorSize;
            }
            set
            {
                _cursorSize = value;
            }
        }

        public override void FlushInputBuffer()
        {
            throw new Exception("Not implemented: PS2EXEHostRawUI.FlushInputBuffer");
        }

        private ConsoleColor _foregroundColor = ConsoleColor.White;
        public override ConsoleColor ForegroundColor
        {
            get
            {
                return _foregroundColor;
            }
            set
            {
                _foregroundColor = value;
            }
        }

        public override BufferCell[,] GetBufferContents(Rectangle rectangle)
        {
            return server.GetBufferContents(rectangle);
        }

        public override bool KeyAvailable
        {
            get
            {
                return false;
            }
        }

        private Size _maxPhysicalWindowSize = new Size(Buffer.MAXCELLS, Buffer.MAXROWS);
        public override Size MaxPhysicalWindowSize
        {
            get
            {
                return _maxPhysicalWindowSize;
            }
        }

        private Size _maxWindowSize = new Size(Buffer.MAXCELLS, Buffer.MAXROWS);
        public override Size MaxWindowSize
        {
            get
            {
                return _maxWindowSize;
            }
        }

        public override KeyInfo ReadKey(ReadKeyOptions options)
        {
            OutputObjectUserRequest o = new OutputObjectUserRequest
            {
                Description = "readkey",
                RequestCorrelationId = Guid.NewGuid().ToString(),
                Data = ""
            };

            ManualResetEvent gotUserRequest = new ManualResetEvent(false);
            KeyInfo ret = new KeyInfo();

            Server.UserInputHandler handler = ( (string inputCorrelationId, string inputData) =>
            {
                try
                {
                    if( string.Compare(inputCorrelationId, o.RequestCorrelationId, true) == 0 )
                    {
                        string json = inputData;
                        ReadKeyResponse rr = JsonConvert.DeserializeObject<ReadKeyResponse>(json);

                        if( rr.cancel )
                        {
                            server.Trace("Cancel event send to pipeline");
                            server.AddInputObject(new InputObjectPipeCancelEvent());
                            gotUserRequest.Set();
                            return;
                        }

                        ControlKeyStates cks = 0;
                        if( rr.ctrlKey == true )
                            cks |= ControlKeyStates.LeftCtrlPressed | ControlKeyStates.RightCtrlPressed;
                        if( rr.altKey == true )
                            cks |= ControlKeyStates.LeftAltPressed | ControlKeyStates.RightAltPressed;
                        if( rr.shiftKey == true )
                            cks |= ControlKeyStates.ShiftPressed;
                        ret = new KeyInfo(rr.keyCode, rr.key, cks, false);

                        gotUserRequest.Set();
                    }
                }
                catch( Exception ex )
                {
                    ui.WriteException(ex);
                    server.Trace("Cancel event send to pipeline");
                    server.AddInputObject(new InputObjectPipeCancelEvent());
                    gotUserRequest.Set();
                }
            } );

            server.OnUserInput += handler;
            try
            {
                server.AddOutputObject(o);

                Thread t = new Thread(new ThreadStart(() =>
                {
                    while( !gotUserRequest.WaitOne(0) )
                        Thread.Sleep(500);
                }));

                t.Name = "PSHostRawUI:Readkey";
                t.Start();

                while( t.ThreadState != System.Threading.ThreadState.Aborted && t.ThreadState != System.Threading.ThreadState.Stopped )
                {
                    if( server.PipelineAbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.AbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.StopRequested )
                    {
                        t.Abort();
                        break;
                    }
                    Thread.Sleep(500);
                }
            }
            catch( Exception ex )
            {
                ui.WriteException(ex);
                server.Trace("Cancel event send to pipeline");
                server.AddInputObject(new InputObjectPipeCancelEvent());
                gotUserRequest.Set();
            }
            finally
            {
                server.OnUserInput -= handler;
                server.EndUserResponseMode();
            }

            return ret;
        }

        public override void ScrollBufferContents(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill)
        {
            throw new Exception("Not implemented: ik.PowerShell.PS2EXEHostRawUI.ScrollBufferContents");
        }

        public override void SetBufferContents(Rectangle rectangle, BufferCell fill)
        {
            if( rectangle.Bottom == -1 && rectangle.Top == -1 && rectangle.Right == -1 && rectangle.Left == -1 )
            {
                server.AddOutputObject(new OutputObjectClearHost());
                server.ClearBuffer();
                return;
            }
            throw new Exception("Not implemented: ik.PowerShell.PS2EXEHostRawUI.SetBufferContents(1)");
        }

        public override void SetBufferContents(Coordinates origin, BufferCell[,] contents)
        {
            throw new Exception("Not implemented: ik.PowerShell.PS2EXEHostRawUI.SetBufferContents(2)");
        }

        private Coordinates _windowPosition = new Coordinates(0, 0);
        public override Coordinates WindowPosition
        {
            get
            {
                return _windowPosition;
            }
            set
            {
                _windowPosition = value;
            }
        }

        private Size _windowSize = new Size(100, 100);

        public override Size WindowSize
        {
            get
            {
                return _windowSize;
            }
            set
            {
                _windowSize = value;
            }
        }

        private string _windowTitle = "";

        public override string WindowTitle
        {
            get
            {
                return _windowTitle;
            }
            set
            {
                _windowTitle = value;
            }
        }
    }

    internal class WebPSConsoleHostUI : PSHostUserInterface
    {
        private WebPSConsoleHostRawUI rawUI = null;
        private Server server = null;


        public WebPSConsoleHostUI(Server server)
            : base()
        {
            rawUI = new WebPSConsoleHostRawUI(server, this);
            this.server = server;
        }

        private string ReadLineFromUser(string caption, string message, string name, string defaultvalue)
        {
            ManualResetEvent gotUserRequest = new ManualResetEvent(false);
            string ret = "";

            try
            {
                OutputObjectUserRequest o = new OutputObjectUserRequest
                {
                    Description = "lineinput",
                    RequestCorrelationId = Guid.NewGuid().ToString(),
                    Data = ""
                };

                StringBuilder sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb);

                using( JsonWriter jsonWriter = new JsonTextWriter(sw) )
                {
                    jsonWriter.Formatting = Formatting.Indented;
                    jsonWriter.WriteStartObject();
                    jsonWriter.WritePropertyName("caption");
                    jsonWriter.WriteValue(caption);
                    jsonWriter.WritePropertyName("message");
                    jsonWriter.WriteValue(message);
                    jsonWriter.WritePropertyName("name");
                    jsonWriter.WriteValue(name);
                    jsonWriter.WritePropertyName("defaultValue");
                    jsonWriter.WriteValue(defaultvalue);
                    jsonWriter.WriteEndObject();
                }

                sw.Flush();
                sw.Close();

                o.Data = sb.ToString();

                Server.UserInputHandler handler = ( (string inputCorrelationId, string inputData) =>
                {
                    try
                    {
                        if( string.Compare(inputCorrelationId, o.RequestCorrelationId, true) == 0 )
                        {
                            string json = inputData;
                            LineinputResponse rr = JsonConvert.DeserializeObject<LineinputResponse>(json);

                            if( rr.cancel )
                            {
                                server.Trace("Cancel event send to pipeline");
                                server.AddInputObject(new InputObjectPipeCancelEvent());
                                gotUserRequest.Set();
                                return;
                            }

                            ret = rr.input;

                            gotUserRequest.Set();
                        }
                    }
                    catch( Exception ex )
                    {
                        WriteException(ex);
                        server.Trace("Cancel event send to pipeline");
                        server.AddInputObject(new InputObjectPipeCancelEvent());
                        gotUserRequest.Set();
                    }
                } );

                server.OnUserInput += handler;
                try
                {
                    server.AddOutputObject(o);

                    Thread t = new Thread(new ThreadStart(() =>
                    {
                        while( !gotUserRequest.WaitOne(0) )
                            Thread.Sleep(500);
                    }));

                    t.Name = "PSHostUI:ReadLineFromUser";
                    t.Start();

                    while( t.ThreadState != System.Threading.ThreadState.Aborted && t.ThreadState != System.Threading.ThreadState.Stopped )
                    {
                        if( server.PipelineAbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.AbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.StopRequested )
                        {
                            t.Abort();
                            break;
                        }
                        Thread.Sleep(500);
                    }
                }
                finally
                {
                    server.OnUserInput -= handler;
                    server.EndUserResponseMode();
                }
            }
            catch( Exception ex )
            {
                WriteException(ex);
                server.Trace("Cancel event send to pipeline");
                server.AddInputObject(new InputObjectPipeCancelEvent());
                gotUserRequest.Set();
                ret = "";
            }

            return ret;
        }

        public override Dictionary<string, PSObject> Prompt(string caption, string message, System.Collections.ObjectModel.Collection<FieldDescription> descriptions)
        {
            try
            {
                Dictionary<string, PSObject> ret = new Dictionary<string, PSObject>();
                foreach( FieldDescription cd in descriptions )
                {
                    Type t = null;
                    if( string.IsNullOrEmpty(cd.ParameterAssemblyFullName) )
                        t = typeof(string);
                    else
                        t = Type.GetType(cd.ParameterAssemblyFullName);

                    if( t.IsArray )
                    {
                        Type elementType = t.GetElementType();
                        Type genericListType = Type.GetType("System.Collections.Generic.List" + ( (char)0x60 ).ToString() + "1");
                        genericListType = genericListType.MakeGenericType(new Type[] { elementType });
                        ConstructorInfo constructor = genericListType.GetConstructor(BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);
                        object resultList = constructor.Invoke(null);

                        int index = 0;
                        string data = "";
                        do
                        {
                            try
                            {
                                string defaultVal = "";
                                if( cd.DefaultValue != null )
                                    if( cd.DefaultValue.BaseObject != null )
                                        defaultVal = cd.DefaultValue.BaseObject.ToString();
                                if( elementType.Equals(typeof(System.Management.Automation.PSCredential)) )
                                {
                                    PSCredential cred = PromptForCredential(caption, message, "", "");
                                    genericListType.InvokeMember("Add", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, resultList, new object[] { cred });
                                }
                                else if( elementType.Equals(typeof(System.Security.SecureString)) )
                                {
                                    SecureString secstr = ReadLineAsSecureString();
                                    genericListType.InvokeMember("Add", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, resultList, new object[] { secstr });
                                }
                                else
                                {
                                    data = ReadLineFromUser(caption, message, ( !string.IsNullOrEmpty(cd.Name) ? string.Format("{0}[{1}]: ", cd.Name, index) : "" ), defaultVal);

                                    if( string.IsNullOrEmpty(data) )
                                        break;

                                    object o = System.Convert.ChangeType(data, elementType);

                                    genericListType.InvokeMember("Add", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, resultList, new object[] { o });
                                }
                            }
                            catch( Exception ex )
                            {
                                throw new Exception("Exception in ik.PowerShell.PS2EXEHostUI.Prompt*1");
                            }
                            index++;
                        } while( true );

                        System.Array retArray = (System.Array)genericListType.InvokeMember("ToArray", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance, null, resultList, null);
                        ret.Add(cd.Name, new PSObject(retArray));
                    }
                    else
                    {
                        object o = null;

                        string l = null;
                        try
                        {
                            string defaultVal = "";
                            if( cd.DefaultValue != null )
                                if( cd.DefaultValue.BaseObject != null )
                                    defaultVal = cd.DefaultValue.BaseObject.ToString();

                            if( t.Equals(typeof(System.Management.Automation.PSCredential)) )
                            {
                                o = PromptForCredential(caption, message, "", "");
                            }
                            else if( t.Equals(typeof(System.Security.SecureString)) )
                            {
                                o = ReadLineAsSecureString();
                            }
                            else
                            {
                                l = ReadLineFromUser(caption, message, ( !string.IsNullOrEmpty(cd.Name) ? string.Format("{0}: ", cd.Name) : "" ), defaultVal);

                                if( string.IsNullOrEmpty(l) )
                                    o = cd.DefaultValue;
                                if( o == null )
                                {
                                    o = System.Convert.ChangeType(l, t);
                                }
                            }

                            ret.Add(cd.Name, new PSObject(o));
                        }
                        catch
                        {
                            throw new Exception("Exception in ik.PowerShell.PS2EXEHostUI.Prompt*2");
                        }
                    }
                }
                return ret;
            }
            catch( Exception ex )
            {
                WriteException(ex);
                return null;
            }
            finally
            {
                OutputObjectUserRequest o = new OutputObjectUserRequest
                {
                    Description = "lineinputfinished",
                    RequestCorrelationId = Guid.NewGuid().ToString(),
                    Data = ""
                };
                server.AddOutputObject(o);
            }
        }

        public override int PromptForChoice(string caption, string message, System.Collections.ObjectModel.Collection<ChoiceDescription> choices, int defaultChoice)
        {
            ManualResetEvent gotUserRequest = new ManualResetEvent(false);
            int choice = -1;
            
            try
            {
                OutputObjectUserRequest o = new OutputObjectUserRequest
                {
                    Description = "choice",
                    RequestCorrelationId = Guid.NewGuid().ToString(),
                    Data = ""
                };

                Regex r = new Regex("\\&([^\\&])");

                string htmlForChoice = "";
                int idx = 0;
                foreach( var c in choices )
                {
                    string label = c.Label;
                    while( r.IsMatch(label) )
                        label = r.Replace(label, "$1");
                    htmlForChoice += string.Format("<input type='radio' name='rdio' value='{0}' {2}/>{1}<br/>", idx, label, ( idx == defaultChoice ? "checked='checked'" : "" ));
                    idx++;
                }

                StringBuilder sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb);

                using( JsonWriter jsonWriter = new JsonTextWriter(sw) )
                {
                    jsonWriter.Formatting = Formatting.Indented;
                    jsonWriter.WriteStartObject();
                    jsonWriter.WritePropertyName("html");
                    jsonWriter.WriteValue(htmlForChoice);
                    jsonWriter.WritePropertyName("caption");
                    jsonWriter.WriteValue(caption);
                    jsonWriter.WritePropertyName("message");
                    jsonWriter.WriteValue(message);
                    jsonWriter.WriteEndObject();
                }

                sw.Flush();
                sw.Close();

                o.Data = sb.ToString();

                Server.UserInputHandler handler = ( (string inputCorrelationId, string inputData) =>
                {
                    try
                    {
                        if( string.Compare(inputCorrelationId, o.RequestCorrelationId, true) == 0 )
                        {
                            string json = inputData;

                            ChoiceResponse rr = JsonConvert.DeserializeObject<ChoiceResponse>(json);

                            if( rr.cancel )
                            {
                                server.Trace("Cancel event send to pipeline");
                                server.AddInputObject(new InputObjectPipeCancelEvent());
                                gotUserRequest.Set();
                                return;
                            }

                            choice = rr.choice;

                            gotUserRequest.Set();
                        }
                    }
                    catch( Exception ex )
                    {
                        WriteException(ex);
                        server.Trace("Cancel event send to pipeline");
                        server.AddInputObject(new InputObjectPipeCancelEvent());
                        gotUserRequest.Set();
                    }
                } );

                server.OnUserInput += handler;
                try
                {
                    server.AddOutputObject(o);

                    Thread t = new Thread(new ThreadStart(() =>
                    {
                        while( !gotUserRequest.WaitOne(0) )
                            Thread.Sleep(500);
                    }));

                    t.Name = "PSHostUI:PromptForChoice";
                    t.Start();

                    while( t.ThreadState != System.Threading.ThreadState.Aborted && t.ThreadState != System.Threading.ThreadState.Stopped )
                    {
                        if( server.PipelineAbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.AbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.StopRequested )
                        {
                            t.Abort();
                            break;
                        }
                        Thread.Sleep(500);
                    }
                }
                finally
                {
                    server.OnUserInput -= handler;
                    server.EndUserResponseMode();
                }
            }
            catch( Exception ex )
            {
                WriteException(ex);
                server.Trace("Cancel event send to pipeline");
                server.AddInputObject(new InputObjectPipeCancelEvent());
                gotUserRequest.Set();
                choice = -1;
            }

            return choice;
        }

        public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName)
        {
            return PromptForCredential(caption, message, userName, targetName, PSCredentialTypes.Default | PSCredentialTypes.Domain | PSCredentialTypes.Generic, PSCredentialUIOptions.None);
        }

        public override SecureString ReadLineAsSecureString()
        {
            ManualResetEvent gotUserRequest = new ManualResetEvent(false);
            SecureString secstr = null;

            try
            {
                OutputObjectUserRequest o = new OutputObjectUserRequest
                {
                    Description = "secstr",
                    RequestCorrelationId = Guid.NewGuid().ToString(),
                    Data = ""
                };

                Server.UserInputHandler handler = ( (string inputCorrelationId, string inputData) =>
                {
                    try
                    {
                        if( string.Compare(inputCorrelationId, o.RequestCorrelationId, true) == 0 )
                        {
                            string json = inputData;

                            SecureStringResponse rr = JsonConvert.DeserializeObject<SecureStringResponse>(json);

                            if( rr.cancel )
                            {
                                server.Trace("Cancel event send to pipeline");
                                server.AddInputObject(new InputObjectPipeCancelEvent());
                                gotUserRequest.Set();
                                return;
                            }

                            secstr = ConvertToSecStr(rr.secstr);

                            gotUserRequest.Set();
                        }
                    }
                    catch( Exception ex )
                    {
                        WriteException(ex);
                        server.Trace("Cancel event send to pipeline");
                        server.AddInputObject(new InputObjectPipeCancelEvent());
                        gotUserRequest.Set();
                    }
                } );

                server.OnUserInput += handler;
                try
                {
                    server.AddOutputObject(o);

                    Thread t = new Thread(new ThreadStart(() =>
                    {
                        while( !gotUserRequest.WaitOne(0) )
                            Thread.Sleep(500);
                    }));

                    t.Name = "PSHostUI:ReadLineAsSecureString";
                    t.Start();

                    while( t.ThreadState != System.Threading.ThreadState.Aborted && t.ThreadState != System.Threading.ThreadState.Stopped )
                    {
                        if( server.PipelineAbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.AbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.StopRequested )
                        {
                            t.Abort();
                            break;
                        }
                        Thread.Sleep(500);
                    }
                }
                finally
                {
                    server.OnUserInput -= handler;
                    server.EndUserResponseMode();
                }
            }
            catch( Exception ex )
            {
                WriteException(ex);
                server.Trace("Cancel event send to pipeline");
                server.AddInputObject(new InputObjectPipeCancelEvent());
                gotUserRequest.Set();
                secstr = null;
            }

            return secstr;
        }

        public override PSCredential PromptForCredential(string caption, string message, string userName, string targetName, PSCredentialTypes allowedCredentialTypes, PSCredentialUIOptions options)
        {
            ManualResetEvent gotUserRequest = new ManualResetEvent(false);
            PSCredential cred = null;

            try
            {
                OutputObjectUserRequest o = new OutputObjectUserRequest
                {
                    Description = "credentials",
                    RequestCorrelationId = Guid.NewGuid().ToString(),
                    Data = ""
                };

                Regex r = new Regex("\\&([^\\&])");

                StringBuilder sb = new StringBuilder();
                StringWriter sw = new StringWriter(sb);

                using( JsonWriter jsonWriter = new JsonTextWriter(sw) )
                {
                    jsonWriter.Formatting = Formatting.Indented;
                    jsonWriter.WriteStartObject();
                    jsonWriter.WritePropertyName("userName");
                    jsonWriter.WriteValue(userName);
                    jsonWriter.WritePropertyName("targetName");
                    jsonWriter.WriteValue(targetName);
                    jsonWriter.WritePropertyName("caption");
                    jsonWriter.WriteValue(caption);
                    jsonWriter.WritePropertyName("message");
                    jsonWriter.WriteValue(message);
                    jsonWriter.WriteEndObject();
                }

                sw.Flush();
                sw.Close();

                o.Data = sb.ToString();

                Server.UserInputHandler handler = ( (string inputCorrelationId, string inputData) =>
                {
                    try
                    {
                        if( string.Compare(inputCorrelationId, o.RequestCorrelationId, true) == 0 )
                        {
                            string json = inputData;

                            CredentialResponse rr = JsonConvert.DeserializeObject<CredentialResponse>(json);

                            if( rr.cancel )
                            {
                                server.Trace("Cancel event send to pipeline");
                                server.AddInputObject(new InputObjectPipeCancelEvent());
                                gotUserRequest.Set();
                                return;
                            }

                            SecureString pwd = ConvertToSecStr(rr.pwd);
                            cred = new PSCredential(rr.usr, pwd);

                            gotUserRequest.Set();
                        }
                    }
                    catch( Exception ex )
                    {
                        WriteException(ex);
                        server.Trace("Cancel event send to pipeline");
                        server.AddInputObject(new InputObjectPipeCancelEvent());
                        gotUserRequest.Set();
                    }
                } );

                server.OnUserInput += handler;
                try
                {
                    server.AddOutputObject(o);

                    Thread t = new Thread(new ThreadStart(() =>
                    {
                        while( !gotUserRequest.WaitOne(0) )
                            Thread.Sleep(500);
                    }));

                    t.Name = "PSHostUI:PromptForCredential";
                    t.Start();

                    while( t.ThreadState != System.Threading.ThreadState.Aborted && t.ThreadState != System.Threading.ThreadState.Stopped )
                    {
                        if( server.PipelineAbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.AbortRequested || Thread.CurrentThread.ThreadState == System.Threading.ThreadState.StopRequested )
                        {
                            t.Abort();
                            break;
                        }
                        Thread.Sleep(500);
                    }
                }
                finally
                {
                    server.OnUserInput -= handler;
                    server.EndUserResponseMode();
                }
            }
            catch( Exception ex )
            {
                WriteException(ex);
                server.Trace("Cancel event send to pipeline");
                server.AddInputObject(new InputObjectPipeCancelEvent());
                gotUserRequest.Set();
                cred = null;
            }

            return cred;
        }

        public override PSHostRawUserInterface RawUI
        {
            get
            {
                return rawUI;
            }
        }

        public override string ReadLine()
        {
            return ReadLineFromUser("Please enter the data", "", "", "");
        }

        private System.Security.SecureString ConvertToSecStr(string str)
        {
            System.Security.SecureString x = new System.Security.SecureString();
            foreach( char c in str.ToCharArray() )
                x.AppendChar(c);
            return x;
        }

        public void WriteException(Exception ex)
        {
            WriteErrorLine(ex.Message);
        }

        public override void WriteLine()
        {
            server.WriteFromPSEnvironment("", DefaultForegroundColor, DefaultBackgroundColor, false);
        }

        public override void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string message)
        {
            server.WriteFromPSEnvironment(message, foregroundColor, backgroundColor, true);
        }

        public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string message)
        {
            server.WriteFromPSEnvironment(message, foregroundColor, backgroundColor, false);
        }

        public override void Write(string message)
        {
            server.WriteFromPSEnvironment(message, DefaultForegroundColor, DefaultBackgroundColor, false);
        }

        public override void WriteDebugLine(string message)
        {
            server.WriteFromPSEnvironment(message, DebugForegroundColor, DebugBackgroundColor, true);
        }

        public override void WriteErrorLine(string message)
        {
            server.WriteFromPSEnvironment(message, ErrorForegroundColor, ErrorBackgroundColor, true);
        }

        public override void WriteLine(string message)
        {
            server.WriteFromPSEnvironment(message, DefaultForegroundColor, DefaultBackgroundColor, true);
        }

        public override void WriteProgress(long sourceId, ProgressRecord record)
        {
            throw new NotImplementedException();
        }

        public override void WriteVerboseLine(string message)
        {
            server.WriteFromPSEnvironment(message, VerboseForegroundColor, VerboseBackgroundColor, true);
        }

        public override void WriteWarningLine(string message)
        {
            server.WriteFromPSEnvironment(message, WarningForegroundColor, WarningBackgroundColor, true);
        }

        public ConsoleColor DefaultBackgroundColor
        {
            get
            {
                return server.DefaultBackgroundColor;
            }
            set
            {
                server.DefaultBackgroundColor = value;
            }
        }
        public ConsoleColor DefaultForegroundColor
        {
            get
            {
                return server.DefaultForegroundColor;
            }
            set
            {
                server.DefaultForegroundColor = value;
            }
        }
        public ConsoleColor DebugBackgroundColor
        {
            get;
            set;
        }
        public ConsoleColor DebugForegroundColor
        {
            get;
            set;
        }
        public ConsoleColor ErrorBackgroundColor
        {
            get;
            set;
        }
        public ConsoleColor ErrorForegroundColor
        {
            get;
            set;
        }
        public ConsoleColor ProgressBackgroundColor
        {
            get;
            set;
        }
        public ConsoleColor ProgressForegroundColor
        {
            get;
            set;
        }
        public ConsoleColor VerboseBackgroundColor
        {
            get;
            set;
        }
        public ConsoleColor VerboseForegroundColor
        {
            get;
            set;
        }
        public ConsoleColor WarningBackgroundColor
        {
            get;
            set;
        }
        public ConsoleColor WarningForegroundColor
        {
            get;
            set;
        }

    }


    internal class PrivateData
    {
        private WebPSConsoleHostUI ui;

        public PrivateData(WebPSConsoleHostUI ui)
        {
            if( ui == null )
            {
                throw new ArgumentNullException("ui");
            }
            this.ui = ui;
        }

        public ConsoleColor DefaultBackgroundColor
        {
            get
            {
                return this.ui.DefaultBackgroundColor;
            }
            set
            {
                this.ui.DefaultBackgroundColor = value;
            }
        }

        public ConsoleColor DefaultForegroundColor
        {
            get
            {
                return this.ui.DefaultForegroundColor;
            }
            set
            {
                this.ui.DefaultForegroundColor = value;
            }
        }


        public ConsoleColor DebugBackgroundColor
        {
            get
            {
                return this.ui.DebugBackgroundColor;
            }
            set
            {
                this.ui.DebugBackgroundColor = value;
            }
        }

        public ConsoleColor DebugForegroundColor
        {
            get
            {
                return this.ui.DebugForegroundColor;
            }
            set
            {
                this.ui.DebugForegroundColor = value;
            }
        }

        public ConsoleColor ErrorBackgroundColor
        {
            get
            {
                return this.ui.ErrorBackgroundColor;
            }
            set
            {
                this.ui.ErrorBackgroundColor = value;
            }
        }

        public ConsoleColor ErrorForegroundColor
        {
            get
            {
                return this.ui.ErrorForegroundColor;
            }
            set
            {
                this.ui.ErrorForegroundColor = value;
            }
        }

        public ConsoleColor ProgressBackgroundColor
        {
            get
            {
                return this.ui.ProgressBackgroundColor;
            }
            set
            {
                this.ui.ProgressBackgroundColor = value;
            }
        }

        public ConsoleColor ProgressForegroundColor
        {
            get
            {
                return this.ui.ProgressForegroundColor;
            }
            set
            {
                this.ui.ProgressForegroundColor = value;
            }
        }

        public ConsoleColor VerboseBackgroundColor
        {
            get
            {
                return this.ui.VerboseBackgroundColor;
            }
            set
            {
                this.ui.VerboseBackgroundColor = value;
            }
        }

        public ConsoleColor VerboseForegroundColor
        {
            get
            {
                return this.ui.VerboseForegroundColor;
            }
            set
            {
                this.ui.VerboseForegroundColor = value;
            }
        }

        public ConsoleColor WarningBackgroundColor
        {
            get
            {
                return this.ui.WarningBackgroundColor;
            }
            set
            {
                this.ui.WarningBackgroundColor = value;
            }
        }

        public ConsoleColor WarningForegroundColor
        {
            get
            {
                return this.ui.WarningForegroundColor;
            }
            set
            {
                this.ui.WarningForegroundColor = value;
            }
        }
    }

    internal class WebPSConsoleHost : PSHost
    {
        private WebPSConsoleApp parent;
        private WebPSConsoleHostUI ui = null;

        private PrivateData privateData = null;
        private PSObject privateDataProxy = null;

        private CultureInfo originalCultureInfo =
            System.Threading.Thread.CurrentThread.CurrentCulture;

        private CultureInfo originalUICultureInfo =
            System.Threading.Thread.CurrentThread.CurrentUICulture;

        private Guid myId = Guid.NewGuid();

        public WebPSConsoleHost(WebPSConsoleApp app, WebPSConsoleHostUI ui)
        {
            this.parent = app;
            this.ui = ui;

            privateData = new PrivateData(ui);

            privateData.DefaultBackgroundColor = ConsoleColor.Black;
            privateData.DebugBackgroundColor = ConsoleColor.Black;
            privateData.VerboseBackgroundColor = ConsoleColor.Black;
            privateData.ErrorBackgroundColor = ConsoleColor.Black;
            privateData.WarningBackgroundColor = ConsoleColor.Black;
            privateData.ProgressBackgroundColor = ConsoleColor.DarkCyan;

            privateData.DefaultForegroundColor = ConsoleColor.White;
            privateData.DebugForegroundColor = ConsoleColor.Yellow;
            privateData.VerboseForegroundColor = ConsoleColor.Yellow;
            privateData.ErrorForegroundColor = ConsoleColor.Red;
            privateData.WarningForegroundColor = ConsoleColor.Yellow;
            privateData.ProgressForegroundColor = ConsoleColor.Yellow;

            privateDataProxy = PSObject.AsPSObject(privateData);
        }

        public override PSObject PrivateData
        {
            get
            {
                return this.privateDataProxy;
            }
        }


        public override System.Globalization.CultureInfo CurrentCulture
        {
            get
            {
                return this.originalCultureInfo;
            }
        }

        public override System.Globalization.CultureInfo CurrentUICulture
        {
            get
            {
                return this.originalUICultureInfo;
            }
        }

        public override Guid InstanceId
        {
            get
            {
                return this.myId;
            }
        }

        public override string Name
        {
            get
            {
                return "WebPSConsole";
            }
        }

        public override PSHostUserInterface UI
        {
            get
            {
                return ui;
            }
        }

        public override Version Version
        {
            get
            {
                return new Version(1, 0, 0, 0);
            }
        }

        public override void EnterNestedPrompt()
        {
        }

        public override void ExitNestedPrompt()
        {
        }

        public override void NotifyBeginApplication()
        {
            return;
        }

        public override void NotifyEndApplication()
        {
            return;
        }

        public override void SetShouldExit(int exitCode)
        {
            this.parent.ShouldExit = true;
            this.parent.ExitCode = exitCode;
        }
    }



    internal interface WebPSConsoleApp
    {
        bool ShouldExit
        {
            get;
            set;
        }
        int ExitCode
        {
            get;
            set;
        }
    }


    internal class WebPSConsole : WebPSConsoleApp
    {

        private bool shouldExit;

        private int exitCode;

        public bool ShouldExit
        {
            get
            {
                return this.shouldExit;
            }
            set
            {
                this.shouldExit = value;
            }
        }

        public int ExitCode
        {
            get
            {
                return this.exitCode;
            }
            set
            {
                this.exitCode = value;
            }
        }
    }
}